package org.activequant.math.algorithms;

/**
 * Exponential Moving Average accumulator.
 * Takes input as a series of measurements, and produces average signal by applying
 * EMA. <br/>
 * Holds the following associated variables:
 * <ul>
 * <li>period(int)</li>
 * <li>value(double)</li>
 * <li>numSamples(int)</li>
 * <li>lambda(double)</li>
 * <li>correction(double)</li>
 * </ul>
 * <p>
 * <b>History:</b><br>
 *  - [14.12.2007] Created (Mike Kroutikov)<br>
 *
 *  @author Mike Kroutikov
 *  
 *  @see WEMAAccumulator
 */
public class EMAAccumulator {
	/**
	 * private int period;
	 */
	private int period;
	/**
	 * private double value;
	 */
	private double value;
	/**
	 * private int numSamples;
	 */
	private int numSamples;
	/**
	 * private double lambda = 0.0;
	 */
	private double lambda = 0.0;
	/**
	 * private double correction = 1.;
	 */
	private double correction = 1.; // small sample correction

	/**
	 * Averaging period (in "steps"). Measurements that are older than
	 * this does not contribute much to the output value.<br/>
	 * returns the associated period(int)
	 * @return averaging period.
	 */
	public int getPeriod() { 
		return period; 
	}
	/**
	 * Sets the averaging period.
	 * sets the associated period(int) with the given val(int), and sets the associated lambda(double) to 1-(1/period)
	 * @param val averaging period.
	 */
	public void setPeriod(int val) { 
		period = val; 
		lambda = 1. - 1. / period;
	}

	/**
	 * Computed average. This value gets re-computed after every measurement.<br/>
	 * returns the associated value(double)
	 * @return computed value.
	 */
	public double getMeanValue() {
		return value; 
	}

	/**
	 * Total number of samples that this accumulator received.
	 * This value is recomputed after every measurement. One can check this and
	 * compare with the {@link #getPeriod() period} to get an idea if
	 * the accumulator has enough data to produce meaningful average.<br/>
	 * returns the associated numSamples(int)
	 * @return total length.
	 */
	public int getNumSamples() { return numSamples; }
	
	/**
	 * Call this to pass next measurement.
	 * After this function returns, following
	 * properties will be recomputed: {@link #getNumSamples() numSamples},
	 * {@link #getMeanValue() meanValue}.<br/>
	 * this methods invokes:<br/>
	 * <strong>correction *= lambda</strong><br/>
	 * <strong>value += (input - value) * (1. - lambda) / (1. - correction)</strong><br/>
	 * <strong>numSamples ++</strong>
	 * @param input value of the signal.
	 */
	public void accumulate(double input) {
		correction *= lambda;

		value += (input - value) * (1. - lambda) / (1. - correction);
		
		numSamples ++;
	}
	/**
	 * returns a String in the form: "${value}(${numSamples} samples)"
	 */
	public String toString() {
		return "" + value + "(" + numSamples + " samples)";
	}
}
